﻿using Minio;
using Minio.DataModel;
using Minio.DataModel.Args;
using Minio.Exceptions;
using PmSoft.Core.Extensions;

namespace PmSoft.Core.FileStorage.Providers;

/// <summary>
/// MinIO 文件存储提供商，实现了 IFileStorageProvider 接口
/// </summary>
public class MinioStorageProvider : IFileStorageProvider
{
	private readonly IMinioClient _minioClient;

	/// <summary>
	/// 构造函数，初始化 MinIO 客户端
	/// </summary>
	/// <param name="endpoint">MinIO 服务器地址，例如 "localhost:9000"</param>
	/// <param name="accessKey">访问密钥，用于身份验证</param>
	/// <param name="secretKey">秘密密钥，用于身份验证</param>
	public MinioStorageProvider(string endpoint, string accessKey, string secretKey)
	{
		_minioClient = new MinioClient()
			.WithEndpoint(endpoint)
			.WithCredentials(accessKey, secretKey)
			.Build();
		TestConnection(); // 构造函数中测试连接
	}

	/// <summary>
	/// 异步保存文件到 MinIO
	/// </summary>
	/// <param name="args">保存文件参数</param>
	/// <returns>表示异步操作的任务</returns>
	public async Task SaveFileAsync(SaveFileArgs args)
	{
		await EnsureBucketExistsAsync(args.BucketName); // 确保存储桶存在
		var objectName = string.IsNullOrEmpty(args.Path) ? args.ObjectName : CombinePath(args.Path, args.ObjectName); // 组合文件路径和名称
		using var stream = new MemoryStream(args.Data); // 创建内存流
		try
		{
			await _minioClient.PutObjectAsync(new PutObjectArgs()
			.WithBucket(args.BucketName)
			.WithObject(objectName)
			.WithStreamData(stream)
			.WithObjectSize(stream.Length)); // 上传文件
		}
		catch (MinioException ex)
		{
			throw new InvalidOperationException($"保存文件到 MinIO 失败: {ex.Message}", ex);
		}
		catch (Exception ex)
		{
			throw new InvalidOperationException($"保存文件时发生未知错误: {ex.Message}", ex);
		}
	}

	/// <summary>
	/// 同步保存文件到 MinIO
	/// </summary>
	/// <param name="args">保存文件参数</param>
	public void SaveFile(SaveFileArgs args)
	{
		SaveFileAsync(args).GetAwaiter().GetResult(); // 调用异步方法并等待结果
	}

	/// <summary>
	/// 异步从 MinIO 删除文件
	/// </summary>
	/// <param name="args">删除文件参数</param>
	/// <returns>表示异步操作的任务</returns>
	public async Task DeleteFileAsync(DeleteFileArgs args)
	{
		try
		{
			var objectName = CombinePath(args.Path, args.ObjectName); // 组合文件路径和名称
		await _minioClient.RemoveObjectAsync(new RemoveObjectArgs()
			.WithBucket(args.BucketName)
			.WithObject(objectName)); // 删除文件
		}
		catch (MinioException ex)
		{
			throw new InvalidOperationException($"删除 MinIO 文件失败: {ex.Message}", ex);
		}
	}

	/// <summary>
	/// 同步从 MinIO 删除文件
	/// </summary>
	/// <param name="args">删除文件参数</param>
	public void DeleteFile(DeleteFileArgs args)
	{
		DeleteFileAsync(args).GetAwaiter().GetResult(); // 调用异步方法并等待结果
	}

	/// <summary>
	/// 异步判断文件在 MinIO 中是否存在
	/// </summary>
	/// <param name="args">判断文件是否存在参数</param>
	/// <returns>布尔值，表示文件是否存在</returns>
	public async Task<bool> FileExistsAsync(FileExistsArgs args)
	{
		try
		{
			var objectName = CombinePath(args.Path, args.ObjectName); // 组合文件路径和名称
			await _minioClient.StatObjectAsync(new StatObjectArgs()
				.WithBucket(args.BucketName)
				.WithObject(objectName)); // 检查文件状态
			return true;
		}
		catch (MinioException ex) when (ex.Response?.Code == "NoSuchKey")
		{
			return false;
		}
		catch (MinioException ex)
		{
			throw new InvalidOperationException($"检查 MinIO 文件存在性失败: {ex.Message}", ex);
		}
	}

	/// <summary>
	/// 同步判断文件在 MinIO 中是否存在
	/// </summary>
	/// <param name="args">判断文件是否存在参数</param>
	/// <returns>布尔值，表示文件是否存在</returns>
	public bool FileExists(FileExistsArgs args)
	{
		return FileExistsAsync(args).GetAwaiter().GetResult(); // 调用异步方法并等待结果
	}

	/// <summary>
	/// 异步从 MinIO 获取文件
	/// </summary>
	/// <param name="args">获取文件参数</param>
	/// <returns>文件数据对象，如果文件不存在则返回 null</returns>
	public async Task<FileData?> GetFileAsync(GetFileArgs args)
	{
		try
		{
			var objectName = CombinePath(args.Path, args.ObjectName); // 组合文件路径和名称
			using var ms = new MemoryStream(); // 创建内存流
			await _minioClient.GetObjectAsync(new GetObjectArgs()
				.WithBucket(args.BucketName)
				.WithObject(objectName)
				.WithCallbackStream(stream => stream.CopyToAsync(ms))); // 下载文件到内存流
			return new FileData { Data = ms.ToArray() }; // 返回文件数据
		}
		catch (MinioException ex) when (ex.Response?.Code == "NoSuchKey")
		{
			return null;
		}
		catch (MinioException ex)
		{
			throw new InvalidOperationException($"获取 MinIO 文件失败: {ex.Message}", ex);
		}
	}

	/// <summary>
	/// 同步从 MinIO 获取文件
	/// </summary>
	/// <param name="args">获取文件参数</param>
	/// <returns>文件数据对象，如果文件不存在则返回 null</returns>
	public FileData? GetFile(GetFileArgs args)
	{
		return GetFileAsync(args).GetAwaiter().GetResult(); // 调用异步方法并等待结果
	}

	/// <summary>
	/// 异步移动 MinIO 中的文件（通过复制和删除实现）
	/// </summary>
	/// <param name="args">移动文件参数</param>
	/// <returns>表示异步操作的任务</returns>
	public async Task MoveFileAsync(MoveFileArgs args)
	{
		try
		{
			// 复制文件到目标位置
			var copyArgs = new CopyObjectArgs()
				.WithBucket(args.DestBucketName)
				.WithObject(args.DestObjectName)
				.WithCopyObjectSource(new CopySourceObjectArgs()
					.WithBucket(args.SourceBucketName)
					.WithObject(args.SourceObjectName));
			await _minioClient.CopyObjectAsync(copyArgs).ConfigureAwait(false);

			// 删除源文件
			var deleteArgs = new RemoveObjectArgs()
				.WithBucket(args.SourceBucketName)
				.WithObject(args.SourceObjectName);
			await _minioClient.RemoveObjectAsync(deleteArgs).ConfigureAwait(false);
		}
		catch (MinioException ex)
		{
			throw new InvalidOperationException($"移动 MinIO 文件失败: {ex.Message}", ex);
		}
	}

	/// <summary>
	/// 同步移动 MinIO 中的文件
	/// </summary>
	/// <param name="args">移动文件参数</param>
	public void MoveFile(MoveFileArgs args)
	{
		MoveFileAsync(args).GetAwaiter().GetResult();
	}

	/// <summary>
	/// 确保存储桶存在，如果不存在则创建
	/// </summary>
	/// <param name="bucketName">存储桶名称</param>
	/// <returns>表示异步操作的任务</returns>
	private async Task EnsureBucketExistsAsync(string bucketName)
	{
		try
		{
			var found = await _minioClient.BucketExistsAsync(new BucketExistsArgs()
				.WithBucket(bucketName));
			if (!found)
			{
				await _minioClient.MakeBucketAsync(new MakeBucketArgs()
					.WithBucket(bucketName));
			}
		}
		catch (MinioException ex)
		{
			throw new InvalidOperationException($"确保存储桶存在失败: {ex.Message}", ex);
		}
	}

	private void TestConnection()
	{
		try
		{
			_minioClient.ListBucketsAsync().GetAwaiter().GetResult();
		}
		catch (Exception ex)
		{
			throw new InvalidOperationException($"无法连接到 MinIO 服务器: {ex.Message}", ex);
		}
	}

	/// <summary>
	/// 组合文件路径和文件名，确保路径格式正确
	/// </summary>
	/// <param name="path">文件路径</param>
	/// <param name="objectName">文件名</param>
	/// <returns>完整的对象名称</returns>
	private string CombinePath(string path, string objectName)
	{
		return string.IsNullOrEmpty(path) ? objectName : $"{path.TrimEnd('/')}/{objectName}";
	}
}